/*
 *  Nautilus-p7zip 
 * 
 *  Copyright (C) Lincoln Zhang.
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU General Public
 *  License as published by the Free Software Foundation; either
 *  version 2 of the License, or (at your option) any later version.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Library General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public
 *  License along with this library; if not, write to the Free
 *  Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 *  Author: Lincoln Zhang <CrazyPandar@gmail.com> 
 * 
 */

#include <string.h>
#include <libnautilus-extension/nautilus-extension-types.h>
#include <libnautilus-extension/nautilus-file-info.h>
#include <libnautilus-extension/nautilus-menu-provider.h>
#include <cf_std.h>
#include "p7zip_helper.h"
#include "nautilus-p7zip.h"
#include "p7zg.h"
#include "p7zg_log.h"
#include "p7zg_cui.h"

static GObjectClass *parent_class;
static GtkWidget* ParentWindow;
static int nautilus_file_info_get_uri_without_scheme(NautilusFileInfo *file, CFString* outUri)
{
	const char* tmpC;
	char* uri;
	
    uri = nautilus_file_info_get_uri(file);
    if(!uri){
    	P7ZG_ERR_OUT(ERR_OUT, "\n");
    }
    tmpC = strstr(uri, "://");
    if(!tmpC){
    	goto ERR_FREE_URI;
    }
    tmpC += 3;
    CFStringSafeCp(outUri, tmpC);
    g_free(uri);
    return 0;
ERR_FREE_URI:
	g_free(uri);
ERR_OUT:
	return -1;
}

int nautilus_file_info_get_parent_uri_without_scheme(NautilusFileInfo *file, CFString *outUri)
{
	const char* tmpC;
	char* uri;
	
    uri = nautilus_file_info_get_parent_uri(file);
    if(!uri){
    	P7ZG_ERR_OUT(ERR_OUT, "\n");
    }
    tmpC = strstr(uri, "://");
    if(!tmpC){
    	goto ERR_FREE_URI;
    }
    tmpC += 3;
    CFStringSafeCp(outUri, tmpC);
    g_free(uri);
    return 0;
ERR_FREE_URI:
	g_free(uri);
ERR_OUT:
	return -1;
}

char** FileVNew(GList* fileVL, int* size)
{
    char* *ret;
    int len;
    GList *scan;
    int i;
    
    if((len = g_list_length(fileVL))>0){
        ret = (char**)CFMalloc(sizeof(char*)*len);
        for(scan=fileVL, i=0; scan; scan=scan->next, i++){
        	char *tmpC, *uri;
            NautilusFileInfo *file = scan->data;
            
            uri = nautilus_file_info_get_uri(file);
            if(!uri){
            	ret[i] = NULL;
            	break;
            }
            tmpC = strstr(uri, "://");
            if(!tmpC){
            	ret[i] = NULL;
            }else{
            	tmpC += 3;
            	ret[i] = CFMalloc(sizeof(char)*strlen(tmpC)+1);
            	strcpy(ret[i], tmpC);
            }
            g_free(uri);
        }
        *size = len;
        return ret;
    }else
        return NULL;
}

int FileVFree(char** fileV, int size)
{
	int i;
	
    if(!fileV)
        return -1;
    for(i=0; i<size; i++){
    	if(fileV[i]){
    		P7ZG_DBG("fileV[%d]:%s\n", i, fileV[i]);
    		CFFree(fileV[i]);
    	}
    }
    CFFree(fileV);
    return 0;
}

static void ArchiveXX(GList* files, BOOL withUI, ArchType format)
{
	NautilusFileInfo *firstFile;
    char** fileV;
    int fileVSize;
    int len;
    CFString archiveUri;
    
    len = g_list_length(files);
	firstFile = files->data;
	CFStringInit(&archiveUri, "");
    if(nautilus_file_info_get_uri_without_scheme(firstFile, &archiveUri)){
        goto ERR_EXIT_ARCHIVE_STR;
    }
    P7ZG_DBG("archiveUri: %s\n", CFStringGetStr(&archiveUri));
    switch(format){
        case ARCH_7Z:
            CFStringSafeCat(&archiveUri, ".7z");
            break;
        case ARCH_TAR_GZ:
            CFStringSafeCat(&archiveUri, ".tar.gz");
            break;
        case ARCH_TAR_BZ2:
            CFStringSafeCat(&archiveUri, ".tar.bz2");
            break;
        case ARCH_ZIP:
            CFStringSafeCat(&archiveUri, ".zip");
            break;
    }
    if((fileV = FileVNew(files, &fileVSize))){
        int cRet;
        if(withUI){
            P7zgCUi ui;
            memset(&ui, 0, sizeof(ui));
            P7zgCUiInit(&ui, ParentWindow, len,  CFStringGetStr(&archiveUri), (const char**)fileV);
            P7zgCUiResetTo7z(&ui);
            cRet = P7zgCUiRun(&ui);
            P7zgCUiExit(&ui);
        }else{
            switch(format){
                case ARCH_7Z:
                    cRet = Compress7z(len, (const char**)fileV, CFStringGetStr(&archiveUri), -1, -1, -1, NULL);
                    break;
                case ARCH_TAR_GZ:
                    cRet = CompressTarGzip(len, (const char**)fileV, CFStringGetStr(&archiveUri), -1, -1, -1);
                    break;
                case ARCH_TAR_BZ2:
                    cRet = CompressTarBzip2(len, (const char**)fileV, CFStringGetStr(&archiveUri), -1, -1, -1);
                    break;
                case ARCH_ZIP:
                    cRet = CompressZip(len, (const char**)fileV, CFStringGetStr(&archiveUri), -1, -1, -1, NULL);
                    break;
            }
        }
        if(cRet){
            //Alert();
        }
        P7ZG_DBG("fileVSize=%d\n", fileVSize);
        FileVFree(fileV, fileVSize);
    }
    CFStringExit(&archiveUri);
    return;
ERR_EXIT_ARCHIVE_STR:
    CFStringExit(&archiveUri);
ERR_OUT:
    return;    
}

BOOL IsTarXX(const char* archive)
{	/*.tar.xx*/
	const char* tmpC;
	if((tmpC=strrchr(archive, '.'))
			&& *(tmpC+1)!='\0' 
			&& (tmpC-4>=archive) 
			&& !strncasecmp(tmpC-4, ".tar", 4)){
		return TRUE;
	}
    return FALSE;
}

static int extract_to(const char* archive, const char* to)
{
    if(!archive || !to){
        return -1;
    }
    if(IsTarXX(archive)){
        return ExtractTarXX(archive, to);
    }else{
        P7ZG_DBG("archive: %s\n", archive);
        return Extract(archive, to);
    }
}

static void GetDestDirByUser(GtkWidget* parent, const char* to, CFString* newDestDir)
{
    GtkWidget *dialog;
    dialog = gtk_file_chooser_dialog_new (_("Select where to extract..."),
                          GTK_WINDOW(parent),
                          GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER,
                          GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
                          GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
                          NULL);
    gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER (dialog), TRUE);
    if(to){
        gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER (dialog), to);
    }else{
        P7ZG_MSG("\n");
    }
    //gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (dialog), "");
    CFStringSafeCp(newDestDir, "");
    if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT) {
        char *filename;
        filename = gtk_file_chooser_get_current_folder(GTK_FILE_CHOOSER (dialog));
        CFStringSafeCp(newDestDir, filename);
        g_free(filename);
    }
    gtk_widget_destroy (dialog);
}

static void ExtractXX(NautilusFileInfo* file, BOOL withUI)
{
	CFString uriWS, pUriWS;
	
	CFStringInit(&uriWS, "");
    if(nautilus_file_info_get_uri_without_scheme(file, &uriWS)){
    	goto ERR_OUT;
    }
    CFStringInit(&pUriWS, "");
    if(nautilus_file_info_get_parent_uri_without_scheme(file, &pUriWS)){
    	goto ERR_FREE_URIWS;
    }
    if(withUI){
    	GetDestDirByUser(ParentWindow, CFStringGetStr(&pUriWS), &pUriWS);
    	if(*CFStringGetStr(&pUriWS))
        	extract_to(CFStringGetStr(&uriWS), CFStringGetStr(&pUriWS));
    }else{
    	extract_to(CFStringGetStr(&uriWS), CFStringGetStr(&pUriWS));
    }
    CFStringExit(&pUriWS);
    CFStringExit(&uriWS);
    return;
ERR_FREE_PURIWS:
	CFStringExit(&pUriWS);
ERR_FREE_URIWS:
	CFStringExit(&uriWS);
 ERR_OUT:
 	return;
}

static void nautilus_p7zip_menu_on_actived(NautilusMenuItem* item, gpointer ctx)
{
	GList            *files;
	NautilusFileInfo *firstFile;
	
	files = g_object_get_data(G_OBJECT (item), "files");
	firstFile = files->data;
	P7ZG_DBG("menuType=%d\n", (*((int*)ctx)));
	switch((*((int*)ctx))){
		case N_P7ZIP_MENU_ADD_TO_ARCHIVE:
			ArchiveXX(files, TRUE, ARCH_7Z);
			break;
		case N_P7ZIP_MENU_ADD_TO_7Z:
			ArchiveXX(files, FALSE, ARCH_7Z);
			break;
		case N_P7ZIP_MENU_ADD_TO_TAR_BZ2:
			ArchiveXX(files, FALSE, ARCH_TAR_BZ2);
			break;
		case N_P7ZIP_MENU_EXTRACT:
			ExtractXX(firstFile, TRUE);
			break;
		case N_P7ZIP_MENU_EXTRACT_HERE:
			ExtractXX(firstFile, FALSE);
			break;
	}
}

static NautilusMenuItem* nautilus_p7zip_new_menu_item(const char* name, const char* label, const char* desc, const char* icon, 
		int menuType, GList* files)
{
	NautilusMenuItem *item;
    static int MenuType[]={		
    	N_P7ZIP_MENU_ADD_TO_ARCHIVE,
		N_P7ZIP_MENU_ADD_TO_7Z,
		N_P7ZIP_MENU_ADD_TO_TAR_BZ2,
		N_P7ZIP_MENU_EXTRACT,
		N_P7ZIP_MENU_EXTRACT_HERE
	};
    
    item = nautilus_menu_item_new (name, label, desc, icon);
    g_signal_connect (item, 
      "activate",
      G_CALLBACK (nautilus_p7zip_menu_on_actived),
      &MenuType[menuType]);
    g_object_set_data_full (G_OBJECT (item), 
      "files",
    nautilus_file_info_list_copy (files),
      (GDestroyNotify) nautilus_file_info_list_free);
    return item;
}

static NautilusMenu* nautilus_p7zip_new_menu(GList* files)
{
	NautilusMenu* ret = NULL;
    char* filename;
    CFString autoFilename;
    
	if (files == NULL)
		goto ERR_OUT;
    if((filename = nautilus_file_info_get_name(files->data))){
    	NautilusMenuItem *item;
		CFStringInit(&autoFilename, "");
		ret = nautilus_menu_new();
		if((item=nautilus_p7zip_new_menu_item("NautilusP7zip::AddToArchive",
		                   _("Add to archive..."),
		                   _("Add file(s) to Archive."),
		                   "document-send", 
		                   N_P7ZIP_MENU_ADD_TO_ARCHIVE, files))){
			nautilus_menu_append_item(ret, item);
		}
		
		CFStringSafeCp(&autoFilename, _("Add to "));
		CFStringSafeCat(&autoFilename, filename);
		CFStringSafeCat(&autoFilename, _(".7z"));
		if((item=nautilus_p7zip_new_menu_item("NautilusP7zip::AddTo7z",
		                   CFStringGetStr(&autoFilename),
		                   _("Add to 7z archive here."),
		                   "document-send", 
		                   N_P7ZIP_MENU_ADD_TO_7Z, files))){
			 nautilus_menu_append_item(ret, item);                  
		}
		                   
		CFStringSafeCp(&autoFilename, _("Add to "));
		CFStringSafeCat(&autoFilename, filename);
		CFStringSafeCat(&autoFilename, _(".tar.bz2"));
		if((item=nautilus_p7zip_new_menu_item("NautilusP7zip::AddToTarBz2",
		                   CFStringGetStr(&autoFilename),
		                   _("Add to .tar.bz2 archive here."),
		                   "document-send", 
		                   N_P7ZIP_MENU_ADD_TO_TAR_BZ2, files))){
			 nautilus_menu_append_item(ret, item); 
		}
		                   
		if((files != NULL) && (files->next == NULL)                     /* one file selected */
		        && !nautilus_file_info_is_directory(files->data)){   
		    if((item=nautilus_p7zip_new_menu_item("NautilusP7zip::Extract",
		                       _("Extract ..."),
		                       _("Extract archive to somewhere."),
		                       "document-send", 
		                       N_P7ZIP_MENU_EXTRACT, files))){
			 	nautilus_menu_append_item(ret, item); 
			}
		    if((item=nautilus_p7zip_new_menu_item("NautilusP7zip::ExtractHere",
		                       _("Extract here"),
		                       _("Extract archive to current directory."),
		                       "document-send", 
		                       N_P7ZIP_MENU_EXTRACT_HERE, files))){
			 	nautilus_menu_append_item(ret, item); 
			}
		}
		CFStringExit(&autoFilename);
		g_free(filename);
		return ret;
    }
	return ret;
ERR_OUT:
	return NULL;
}


static GList *
nautilus_p7zip_get_file_items (NautilusMenuProvider *provider,
			      GtkWidget            *window,
			      GList                *files)
{
	GList    *scan;
    GList    *items = NULL;
    NautilusMenuItem *item = NULL;
    NautilusMenu *menu = NULL;
    
	if (files == NULL)
		return NULL;

	for (scan = files; scan; scan = scan->next) {
		NautilusFileInfo *file = scan->data;
		gchar            *scheme;
		gboolean          local;

		scheme = nautilus_file_info_get_uri_scheme (file);
		local = strncmp (scheme, "file", 4) == 0;
		g_free (scheme);

		if (!local)
			return NULL;
	}       
    ParentWindow = window;
    if((item=nautilus_menu_item_new("NautilusP7zip::P7zip", _("P7zip"), _("p7zip sub menu."), "document-send"))){
    	if((menu=nautilus_p7zip_new_menu(files))){
    		nautilus_menu_item_set_submenu(item, menu);
    	}
    	items = g_list_append (items, item);
    }
	return items;
}


static void 
nautilus_p7zip_menu_provider_iface_init (NautilusMenuProviderIface *iface)
{
	iface->get_file_items = nautilus_p7zip_get_file_items;
}


static void 
nautilus_p7zip_instance_init (NautilusP7zip *p7zip)
{
}


static void
nautilus_p7zip_class_init (NautilusP7zipClass *class)
{
	parent_class = g_type_class_peek_parent (class);
}


static GType p7zip_type = 0;


GType
nautilus_p7zip_get_type (void) 
{
	return p7zip_type;
}


void
nautilus_p7zip_register_type (GTypeModule *module)
{
	static const GTypeInfo info = {
		sizeof (NautilusP7zipClass),
		(GBaseInitFunc) NULL,
		(GBaseFinalizeFunc) NULL,
		(GClassInitFunc) nautilus_p7zip_class_init,
		NULL,
		NULL,
		sizeof (NautilusP7zip),
		0,
		(GInstanceInitFunc) nautilus_p7zip_instance_init,
	};

	static const GInterfaceInfo menu_provider_iface_info = {
		(GInterfaceInitFunc) nautilus_p7zip_menu_provider_iface_init,
		NULL,
		NULL
	};

	p7zip_type = g_type_module_register_type (module,
					         G_TYPE_OBJECT,
					         "NautilusP7zip",
					         &info, 0);

	g_type_module_add_interface (module,
				     p7zip_type,
				     NAUTILUS_TYPE_MENU_PROVIDER,
				     &menu_provider_iface_info);
}
